#include <cstdio>
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include "Task.hpp"

using namespace std;

#define ProcNum 5 // 子进程数量

int waitCommand(int fd, bool &quit) // 子进程获取命令
{
    uint32_t a = -1;
    ssize_t s = read(fd, &a, sizeof(uint32_t));
    if (s == 0) // 写方关闭
    {
        close(fd);
        quit = true;
        return -1;
    }
    assert(s != -1);
    assert(s == sizeof(uint32_t)); // 一定读到4字节的命令
    return a;                      // 返回读到的命令
}

void wakeChild(int task_index, pid_t child_pid, int child_fd) // 向这个写fd发送命令，唤醒对应的子进程来执行
{
    cout << "child[" << child_pid << "] do " << task_list[task_index] << " through fd[" << child_fd << "]" << endl;
    write(child_fd, &task_index, sizeof(uint32_t));
}

int main()
{
    Load();
    vector<pair<pid_t, int>> slots; // 保存子进程pid和对应的写端管道描述符
    for (int i = 0; i < ProcNum; i++)
    {
        int pipefd[2] = {0};
        int n = pipe(pipefd); // 创建管道
        assert(n == 0);

        pid_t id = fork(); // 创建子进程
        assert(id != -1);

        if (id == 0) // 子进程
        {
            close(pipefd[1]); // 关闭写端
            bool quit = false;
            while (true)
            {
                int cmd = waitCommand(pipefd[0], quit); // 等待命令
                if (quit)
                {
                    break; // quit表示读到0，子进程可以退出
                }
                assert(cmd >= 0 && cmd < handlerSize());
                cout << "####################################" << endl;
                callbacks[cmd](); // 执行回调任务
                cout << "####################################" << endl;
            }
            close(pipefd[0]);
            exit(0);
        }
        // 父进程
        close(pipefd[0]);                          // 关闭读端
        slots.push_back(make_pair(id, pipefd[1])); // 子进程pid和对应的写端管道描述符
    }
    while (true)
    {
        // cout<<"####################################"<<endl;
        // cout<<"###  1.任务列表   2.选择任务   ######"<<endl;
        // cout<<"####################################"<<endl;
        show();
        cout << "-1  ->  exit:退出" << endl;
        cout << "请选择：";
        int task_index = -1;
        cin >> task_index;
        if (task_index == -1)
            break; // 表示用户想要退出
        if (task_index < 0 || task_index > handlerSize())
        {
            printf("unknown selection:%d\n", task_index);
            continue; // 未知任务编号
        }
        srand((unsigned long)time(NULL) ^ getpid() ^ 114514);                       // 随机种子
        int child_index = rand() % slots.size();                                    // 随机挑选一个子进程
        wakeChild(task_index, slots[child_index].first, slots[child_index].second); // 向该随机选出的子进程发送执行任务的命令
        sleep(1);
    }

    for (const auto &slot : slots)
    {
        close(slot.second); // 关闭父进程打开的写管道
    }

    for (const auto &slot : slots)
    {
        int ret = waitpid(slot.first, NULL, 0); // 等待所有子进程退出
        assert(ret != -1);
    }
    return 0;
}
